package org.levi.demo.mybatis.plugin.plugins;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.levi.demo.mybatis.plugin.annotation.DataAuth;
import org.levi.demo.mybatis.plugin.annotation.DataPermission;
import org.levi.demo.mybatis.plugin.util.AnnotationUtil;
import org.levi.demo.mybatis.plugin.util.StringUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Statement;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class DataPermissionPlugin implements Interceptor {

    public Object intercept(Invocation invocation) throws Throwable {

        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = null;
        if (args[0] instanceof MappedStatement) {
            mappedStatement = (MappedStatement) args[0];
        }
       /* if (mappedStatement != null) {
            System.out.println("origin sql: " + mappedStatement.getBoundSql(args[1]).getSql());
        }*/
        //org.levi.demo.mybatis.plugin.dao.UserDao.findAll
        String methodId = mappedStatement.getId();
        String classInfos = methodId.substring(0, methodId.lastIndexOf("."));
        String methodName = methodId.substring(methodId.lastIndexOf(".") + 1);
        Class mapperClazz = Class.forName(classInfos);
        Method[] declaredMethods = mapperClazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            if (methodName.equals(declaredMethod.getName())) {
                //判断方法上面是否有注解
                DataAuth annotation = declaredMethod.getAnnotation(DataAuth.class);
                if (annotation == null) {
                    //原样执行
                    return invocation.proceed();
                }
                //获取注解上的权限配置
                DataPermission[] permissions = (DataPermission[]) AnnotationUtil.getAnnotationValue(annotation, "permissions");
                if (permissions.length <= 0) {
                    throw new RuntimeException("empty permission!");
                }
                MappedStatement enhanceMapperStatement = getMapperStatement(mappedStatement, new DataPermissionSqlSource(mappedStatement.getBoundSql(args[1])));
                String originSql = mappedStatement.getBoundSql(args[1]).getSql();
                String sql = new DataAuthSqlBuilder(originSql,permissions).getPermissionSql();
                MetaObject msObject = MetaObject.forObject(enhanceMapperStatement, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
                msObject.setValue("sqlSource.boundSql.sql", sql);
                args[0] = enhanceMapperStatement;
                System.out.println("after data permission sql: " + sql);
                return invocation.proceed();
            }
        }
        System.out.println("================>>>>>>>>>>>>>>对方法进行了增强<<<<<<<<<<<<<<<<<<===========");
        return invocation.proceed();
    }

    private MappedStatement getMapperStatement(MappedStatement mappedStatement, DataPermissionSqlSource dataPermissionSqlSource) {

        MappedStatement.Builder builder = new MappedStatement.Builder(mappedStatement.getConfiguration(), mappedStatement.getId(), dataPermissionSqlSource,
                mappedStatement.getSqlCommandType());
        builder.resource(mappedStatement.getResource());
        builder.fetchSize(mappedStatement.getFetchSize());
        builder.statementType(mappedStatement.getStatementType());
        builder.keyGenerator(mappedStatement.getKeyGenerator());
        if (mappedStatement.getKeyProperties() != null && mappedStatement.getKeyProperties().length != 0) {
            StringBuilder keyProperties = new StringBuilder();
            for (String keyProperty : mappedStatement.getKeyProperties()) {
                keyProperties.append(keyProperty).append(",");
            }
            keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
            builder.keyProperty(keyProperties.toString());
        }
        builder.timeout(mappedStatement.getTimeout());
        builder.parameterMap(mappedStatement.getParameterMap());
        builder.resultMaps(mappedStatement.getResultMaps());
        builder.resultSetType(mappedStatement.getResultSetType());
        builder.cache(mappedStatement.getCache());
        builder.flushCacheRequired(mappedStatement.isFlushCacheRequired());
        builder.useCache(mappedStatement.isUseCache());
        return builder.build();

    }

    public Object plugin(Object o) {

        //System.out.println("================>>>>>>>>>>>>>>挂载当前增强<<<<<<<<<<<<<<<<<<===========");
        return Plugin.wrap(o, this);
    }

    public void setProperties(Properties properties) {
        System.out.println("================>>>>>>>>>>>>>>获取properties配置<<<<<<<<<<<<<<<<<<===========");
        properties.list(System.out);
    }

    class DataPermissionSqlSource implements SqlSource {

        private BoundSql boundSql;

        public DataPermissionSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return this.boundSql;
        }
    }



}
